#include <cstdlib>
#include <cmath>
#include <fstream>
#include "lsystem.h"
#include "SDL_draw.h"
const float PI = 3.1415926535897;

bool isUpper (char ch) { return (ch >= 'A' && ch <= 'Z'); }

bool isLetter (char ch) { 
  return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
}

LSystem::LSystem(const Vector2f& pos, const Vector2f& vel,
                 const string &filename,FrameFactory *ff) :
  Sprite(pos, vel, NULL),
  screen(  IOManager::getInstance()->getScreen() ), 
  factory(ff),
  gameData( Gamedata::getInstance() ),
  spriteWidth(0),
  spriteHeight(0),
  screenWidth(gameData->getXmlInt("viewWidth")),
  screenHeight(gameData->getXmlInt("viewHeight")),
  gHandler(new GrammarHandler()),
  parser( filename,gHandler),
  xmldata(),
  spriteSurface(NULL),

  state(State()),

  BLACK( SDL_MapRGB(screen->format, 0x00, 0x00, 0x00) ),
  MAGENTA( SDL_MapRGB(screen->format, 0xff, 0x00, 0xff) ),
  WHITE( SDL_MapRGB(screen->format, 0xff, 0xff, 0xff) ),
  levels(0),
  angle(0),
  startX(0),
  startY(0),
  strokeLength(1),
  strokeWidth(1),
  facing(0),
  strokeColor(BLACK),
  startSymbol('F'),
  grammar(),
  stateStack( ),
  sentence( ) 
  {
   
    cout << "Parsing XML file ... " << endl;
    //parser.parseXML();
    cout << "Loading spec ... " << endl;
    loadSpecification();
    //printLSystem();
    cout << "Building a sentence in the language ... " << endl;
    buildSentence();
    //cout << "The sentence: " << sentence << endl;
    cout << "Making a sprite out of the sentence ... " << endl;
    fillSprite();
    setFrame(factory->getFrame(spriteSurface,filename,spriteWidth, spriteHeight, 0, 0, true));
}

LSystem::~LSystem() {
  std::cout<<"delete lsystem"<<std::endl;
  delete gHandler;
  //SDL_FreeSurface(screen);
}

unsigned LSystem::findVal( const string& val ) const {
  std::map<string, string>::const_iterator ptr = xmldata.find(val);
  if ( ptr == xmldata.end() ) {
    throw string("Couldn't find ")+val;
  }
  string data  = ptr->second;
  return atoi( data.c_str() );
}

void LSystem::loadSpecification() {
  xmldata = parser.getXmlData(); 
  spriteWidth = findVal("spriteWidth");
  spriteHeight = findVal("spriteHeight");

  spriteSurface = 
   SDL_CreateRGBSurface(SDL_SWSURFACE,spriteWidth,spriteHeight,32,0,0,0,0); 

  levels = findVal("levels");
  angle = findVal("angle");
  startX = findVal("startX");
  startY = findVal("startY");
  strokeLength = findVal("strokeLength");
  strokeWidth = findVal("strokeWidth");


  unsigned red = findVal("red");
  unsigned blue = findVal("blue");
  unsigned green = findVal("green");
  strokeColor = SDL_MapRGB(screen->format, red, green, blue);
  try {
    facing = findVal("facing");
    state.facing = facing;
  }
  catch( const string& msg ) {
  }

  std::multimap<string, string>::const_iterator ptr = 
    xmldata.find("startSymbol");
  if ( ptr != xmldata.end() ) {
    string data  = ptr->second;
    startSymbol = data[0];
  }
  else throw string("Couldn't find start symbol!");
  grammar = gHandler->getGrammar(); 

  printGrammar();
}


void LSystem::fillSprite() { 
  SDL_LockSurface(spriteSurface);
  //SDL_FillRect(spriteSurface, NULL, WHITE);
  SDL_FillRect(spriteSurface, NULL, MAGENTA);
  state.x = startX;
  state.y = startY;
  for (unsigned int i = 0; i < sentence.size(); ++i) {
    switch ( sentence[i] ) {
      case '-' :  state.facing -= angle; break;
      case '+' :  state.facing += angle; break;
      case '[' :  stateStack.push(state); break;
      case ']' :  {
        state = stateStack.top();
        stateStack.pop();
        break;
      }
      case 'X' : case 'Y' : case 'f' :  {
        int x1 = state.x +
            static_cast<int>(strokeLength * cos(state.facing*PI/180));
        int y1 = state.y + 
            static_cast<int>(strokeLength * sin(state.facing*PI/180));
        state.x = state.x + (x1-state.x);
        state.y = state.y + (y1-state.y);
        break;
      }
      case 'F' :  case 'A' : case 'B' : {
        int x1 = state.x + 
            static_cast<int>(strokeLength * cos(state.facing*PI/180));
        int y1 = state.y + 
            static_cast<int>(strokeLength * sin(state.facing*PI/180));
        if ( state.x < 0 || state.y < 0 || x1 < 0 || y1 < 0 ) {
          throw string("x, y, x1, or x2 negative; length too long?");
        }
        Draw_Line(spriteSurface, state.x,state.y, x1,y1, strokeColor);
        state.x = state.x + (x1-state.x);
        state.y = state.y + (y1-state.y);
        break;
      }
      default : {
        cout << "sentence size is: " << sentence.size() << endl;
        cout << "i is: " << i << endl;
        throw string("Unhandled switch/case: ")+sentence[i];
      }
    }
  }
  SDL_UnlockSurface(spriteSurface);
}

//void LSystem::draw() const { 
  //SDL_Rect src = { 0, 0, screenWidth, screenHeight };    
  //SDL_Rect dest = {0, 0, screenWidth, screenHeight };
  //SDL_BlitSurface(spriteSurface, &src, screen, &dest);
//}

void LSystem::buildSentence() {
  int count = 0;
  sentence = startSymbol;
  cout <<"====SENTENCE===="<<endl;
  cout << "LEVELS: " << levels << endl;
  cout << "START SYMBOL: " << startSymbol << endl;
  while ( count < levels ) {
    string new_sentence;
    for (unsigned int i = 0; i < sentence.size(); ++i) {
      char ch = sentence[i];
      if ( isUpper(ch) ) {
        new_sentence += grammar.find(string(1,ch))->second;
      }
      else if ( ch == '+' || ch == '-' ) new_sentence+= ch;
      else if ( ch == '[' || ch == ']' ) new_sentence+= ch;
      else if ( ch == 'f' ) new_sentence+= ch;
    }
    sentence = new_sentence;
  cout << "SENTENCE: " << sentence << endl;
    ++count;
  }
    cout <<"====SENTENCE===="<<endl;
}

void LSystem::printGrammar() const {
  std::map<string, string>::const_iterator it = grammar.begin();
  cout << "====Grammar====" << endl;
  cout << "Non-T" << '\t' << "RHS" << endl;
  while ( it != grammar.end() ) {
    cout << it->first << "\t" << it->second << endl;
    ++it;
  }
  cout << "====Grammar====" << endl;
}

void LSystem::printLSystem() const {
  cout << "levels: " << levels << endl;
  cout << "startSymbol: " << startSymbol << endl;
  cout << "startX: " << startX << endl;
  cout << "startY: " << startY << endl;
  cout << "angle: " << angle << endl;
  cout << "facing: " << state.facing << endl;
  cout << "strokeLength: " << strokeLength << endl;
  cout << "strokeWidth: " << strokeWidth << endl;
  cout << "strokecolor: " << strokeColor << endl;
  printGrammar();
}
